package br.edu.ifce.mflj.regras;

import br.edu.ifce.mflj.dados.Casa;
import br.edu.ifce.mflj.dados.Movimento;
import br.edu.ifce.mflj.dados.Peca;

public class RegrasDamas {

	public static Boolean movimentoPermitido( Casa[][] tabuleiro, Casa casaDestino, Peca peca ){
		try {
			// Se a casa de destino tiver uma peça ou não houver variação em X do movimento, o mesmo é inválido
			if( casaDestino.isOcupada() || casaDestino.getColuna().equals( peca.getColuna() ) ){
				return false;

			} else {
				switch( peca.getTipoPeca() ){
					case Peca.PECA_PRETA:
						if( tratarMovimentoPecaPreta( tabuleiro, peca, casaDestino ) ){
							if( casaDestino.getLinha().equals( peca.getLinhaObjetivo() ) ){
								tabuleiro[ casaDestino.getColuna() ][ casaDestino.getLinha() ] = new Casa( casaDestino.getColuna(), casaDestino.getLinha(), Peca.DAMA_PRETA );

							} else {
								tabuleiro[ casaDestino.getColuna() ][ casaDestino.getLinha() ] = new Casa( casaDestino.getColuna(), casaDestino.getLinha(), peca.getTipoPeca() );
							}

							tabuleiro[ peca.getColuna() ][ peca.getLinha() ] = new Casa( peca.getColuna(), peca.getLinha(), null );
							return true;
						}
						break;
					case Peca.PECA_BRANCA:
						if( tratarMovimentoPecaBranca( tabuleiro, peca, casaDestino ) ){
							if( casaDestino.getLinha().equals( peca.getLinhaObjetivo() ) ){
								tabuleiro[ casaDestino.getColuna() ][ casaDestino.getLinha() ] = new Casa( casaDestino.getColuna(), casaDestino.getLinha(), Peca.DAMA_BRANCA );

							} else {
								tabuleiro[ casaDestino.getColuna() ][ casaDestino.getLinha() ] = new Casa( casaDestino.getColuna(), casaDestino.getLinha(), peca.getTipoPeca() );
							}

							tabuleiro[ peca.getColuna() ][ peca.getLinha() ] = new Casa( peca.getColuna(), peca.getLinha(), null );
							return true;
						}
						break;

					case Peca.DAMA_BRANCA:
					case Peca.DAMA_PRETA:
						return tratarMovimentoDama( tabuleiro, peca, casaDestino );

					default:
						return false;
				}
				return false;
			}

		} catch( NullPointerException nullPointerException ){
			return true;

		} catch( ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException ){
			return false;
		}
	}

	private static Boolean tratarMovimentoPecaPreta( Casa[][] tabuleiro, Peca peca, Casa casaDestino ){
		Integer	deslocamentoEmX	= casaDestino.getColuna() - peca.getColuna(),
				deslocamentoEmY	= casaDestino.getLinha() - peca.getLinha();

		if( pecaSubindoEmY( deslocamentoEmY ) ){ // Caso em que a pe�a preta estiver subindo verticalmente
			switch( Math.abs( deslocamentoEmY ) ){
				case 1: // Se subiu apenas uma casa, movimento inv�lido
					return false;
	
				case 2: // S� � permitido a uma pe�a preta voltar duas casas verticalmente se nesse caso houver uma pe�a branca na posi��o correta.
					switch( Math.abs( deslocamentoEmX ) ){
						case 2: // Um pe�a preta s� pode voltar duas casas � esquerda horizontalmente se houver uma pe�a branca na posi��o de ser atacada.
							if( pecaVoltandoEmX( deslocamentoEmX ) ){ // Se a pe�a preta voltar para a esquerda
								return atacouAdversariaSuperiorEsquerda( tabuleiro, peca, Peca.PECA_BRANCA );

							} else if( pecaAvancandoEmX( deslocamentoEmX ) ){
								return atacouAdversariaSuperiorDireita( tabuleiro, peca, Peca.PECA_BRANCA );
							}

						default:
							return false;
					}

				default:
					return false;
			}

		} else if( pecaDescendoEmY( deslocamentoEmY ) ){
			switch( Math.abs( deslocamentoEmY ) ){
				case 1: // Se a pe�a preta descer uma casa para baixo, movimento v�lido.
					return Math.abs( deslocamentoEmX ) == 1;

				case 2: // Se a pe�a preta descer duas casas verticalmente deve ser verificado o deslocamento horizontal
					switch( Math.abs( deslocamentoEmX ) ){
						case 1:
							return false;

						case 2:
							if( pecaAvancandoEmX( deslocamentoEmX ) ){
								return atacouAdversariaInferiorDireita( tabuleiro, peca, Peca.PECA_BRANCA );

							} else if( pecaVoltandoEmX( deslocamentoEmX ) ){
								return atacouAdversariaInferiorEsquerda( tabuleiro, peca, Peca.PECA_BRANCA );

							} else {
								return false;
							}

						default:
							return false;
					}

				default:
					return false;
			}

		} else {
			return false;
		}
	}

	public static Boolean tratarMovimentoPecaBranca( Casa[][] tabuleiro, Peca peca, Casa casaDestino ){
		Integer deslocamentoEmX = casaDestino.getColuna() - peca.getColuna(),
				deslocamentoEmY = casaDestino.getLinha() - peca.getLinha();

		if( pecaSubindoEmY( deslocamentoEmY ) ){
			switch( Math.abs( deslocamentoEmY ) ){
				case 1:
					return Math.abs( deslocamentoEmX ) == 1;
	
				case 2:
					switch( Math.abs( deslocamentoEmX ) ){
						case 2:
							if( pecaAvancandoEmX( deslocamentoEmX ) ){
								return atacouAdversariaSuperiorDireita( tabuleiro, peca, Peca.PECA_PRETA );
		
							} else if( pecaVoltandoEmX( deslocamentoEmX ) ){
								return atacouAdversariaSuperiorEsquerda( tabuleiro, peca, Peca.PECA_PRETA );
		
							} else {
								return false;
							}
		
						default:
							return false;
					}
				default:
					return false;
			}
		} else if( pecaDescendoEmY( deslocamentoEmY ) ){
			switch( Math.abs( deslocamentoEmY ) ){
				case 2:
					switch( Math.abs( deslocamentoEmX ) ){
						case 2:
							if( pecaVoltandoEmX( deslocamentoEmX ) ){
								return atacouAdversariaInferiorEsquerda( tabuleiro, peca, Peca.PECA_PRETA );
		
							} else if( pecaAvancandoEmX( deslocamentoEmX ) ){
								return atacouAdversariaInferiorDireita( tabuleiro, peca, Peca.PECA_PRETA );
							}
						default:
							return false;
					}
				default:
					return false;
			}

		} else {
			return false;
		}
	}
	
	private static Boolean tratarMovimentoDama( Casa[][] tabuleiro, Peca pecaAtacante, Casa casaDestino ){
		int coluna				= pecaAtacante.getColuna(),
			linha				= pecaAtacante.getLinha(),
			colunaFinal			= casaDestino.getColuna(),
			linhaFinal			= casaDestino.getLinha(),
			colunaARemover		= 0,
			linhaARemover		= 0,
			pecasAdversarias	= 0,
			deslocamentoEmX		= Math.abs( casaDestino.getColuna() - pecaAtacante.getColuna() ),
			deslocamentoEmY		= Math.abs( casaDestino.getLinha() - pecaAtacante.getLinha() );

		if( deslocamentoEmX != deslocamentoEmY ){
			return false;

		} else {
			switch( direcaoMovimento( pecaAtacante, casaDestino ) ){
				case Movimento.SUPERIOR_DIREITO:
					for( coluna++, linha--; ( coluna != colunaFinal ) && ( linha != linhaFinal ); coluna++, linha-- ){
						if(	casaComPecaAdversaria( tabuleiro[ coluna ][ linha ], pecaAtacante ) ){
							pecasAdversarias++;
							if( pecasAdversarias == 1 ){
								colunaARemover	= coluna;
								linhaARemover	= linha;
							}
						} else if( casaComPecaAliada( tabuleiro[ coluna ][ linha ], pecaAtacante ) ){
							return false;
						}
					}
					break;
					
				case Movimento.SUPERIOR_ESQUERDO:
					for( coluna--, linha--; ( coluna != colunaFinal ) && ( linha != linhaFinal ); coluna--, linha-- ){
						if(	casaComPecaAdversaria( tabuleiro[ coluna ][ linha ], pecaAtacante ) ){
							pecasAdversarias++;
							if( pecasAdversarias == 1 ){
								colunaARemover	= coluna;
								linhaARemover	= linha;
							}
						} else if( casaComPecaAliada( tabuleiro[ coluna ][ linha ], pecaAtacante ) ){
							return false;
						}
					}
					break;
					
				case Movimento.INFERIOR_DIREITO:
					for( coluna++, linha++; ( coluna != colunaFinal ) && ( linha != linhaFinal ); coluna++, linha++ ){
						if(	casaComPecaAdversaria( tabuleiro[ coluna ][ linha ], pecaAtacante ) ){
							pecasAdversarias++;
							if( pecasAdversarias == 1 ){
								colunaARemover	= coluna;
								linhaARemover	= linha;
							}
						} else if( casaComPecaAliada( tabuleiro[ coluna ][ linha ], pecaAtacante ) ){
							return false;
						}
					}
					break;
					
				case Movimento.INFERIOR_ESQUERDO:
					for( coluna--, linha++; ( coluna != colunaFinal ) && ( linha != linhaFinal ); coluna--, linha++ ){
						if(	casaComPecaAdversaria( tabuleiro[ coluna ][ linha ], pecaAtacante ) ){
							pecasAdversarias++;
							if( pecasAdversarias == 1 ){
								colunaARemover	= coluna;
								linhaARemover	= linha;
							}
						} else if( casaComPecaAliada( tabuleiro[ coluna ][ linha ], pecaAtacante ) ){
							return false;
						}
					}
					break;
			}
			
			if( pecasAdversarias == 1 ){
				tabuleiro[ colunaARemover ][ linhaARemover ] = new Casa( colunaARemover, linhaARemover, null );

			} else if( pecasAdversarias > 1 ){
				return false;
			}

			tabuleiro[ casaDestino.getColuna() ][ casaDestino.getLinha() ] = new Casa( casaDestino.getColuna(), casaDestino.getLinha(), pecaAtacante.getTipoPeca() );
			tabuleiro[ pecaAtacante.getColuna() ][ pecaAtacante.getLinha() ] = new Casa( pecaAtacante.getColuna(), pecaAtacante.getLinha(), null );
			return true;
		}
	}

	private static boolean casaComPecaAliada( Casa casaAtacada, Peca pecaAtacante ) {
		Integer pecaAliada	= -1,
				damaAliada	= -1;

		if( casaAtacada.isOcupada() ){
			pecaAliada	= pecaAtacante.getTipoPeca().equals( Peca.DAMA_PRETA ) ? Peca.PECA_PRETA : Peca.PECA_BRANCA;
			damaAliada	= pecaAtacante.getTipoPeca().equals( Peca.DAMA_BRANCA ) ? Peca.DAMA_BRANCA : Peca.DAMA_PRETA;

			return casaAtacada.getPeca().getTipoPeca().equals( pecaAliada ) || casaAtacada.getPeca().getTipoPeca().equals( damaAliada );
		}

		return false;
	}

	private static boolean casaComPecaAdversaria( Casa casaAtacada, Peca pecaAtacante ) {
		Integer pecaAdversaria	= -1,
				damaAdversaria	= -1;

		if( casaAtacada.isOcupada() ){
			pecaAdversaria	= pecaAtacante.getTipoPeca().equals( Peca.DAMA_BRANCA ) ? Peca.PECA_PRETA : Peca.PECA_BRANCA;
			damaAdversaria	= pecaAtacante.getTipoPeca().equals( Peca.DAMA_BRANCA ) ? Peca.DAMA_PRETA : Peca.DAMA_BRANCA;

			return casaAtacada.getPeca().getTipoPeca().equals( pecaAdversaria ) || casaAtacada.getPeca().getTipoPeca().equals( damaAdversaria );
		}

		return false;
	}
	
	private static int direcaoMovimento( Peca peca, Casa casaDestino ){
		Integer deslocamentoEmX = casaDestino.getColuna() - peca.getColuna(),
				deslocamentoEmY = casaDestino.getLinha() - peca.getLinha();

		if( pecaAvancandoEmX( deslocamentoEmX ) && pecaSubindoEmY( deslocamentoEmY ) ){
			return Movimento.SUPERIOR_DIREITO;
		}

		if( pecaAvancandoEmX( deslocamentoEmX ) && pecaDescendoEmY( deslocamentoEmY ) ){
			return Movimento.INFERIOR_DIREITO;
		}

		if( pecaVoltandoEmX( deslocamentoEmX ) && pecaSubindoEmY( deslocamentoEmY ) ){
			return Movimento.SUPERIOR_ESQUERDO;
		}

		if( pecaVoltandoEmX( deslocamentoEmX ) && pecaDescendoEmY( deslocamentoEmY ) ){
			return Movimento.INFERIOR_ESQUERDO;
		}

		return -1;
	}

	private static Boolean atacouAdversariaSuperiorEsquerda( Casa[][] tabuleiro, Peca peca, Integer tipoPecaAdversaria ){
		return removeuPecaDoTabuleiro( tabuleiro, peca, tipoPecaAdversaria, peca.getColuna() - 1, peca.getLinha() - 1 );
	}

	private static Boolean atacouAdversariaInferiorEsquerda( Casa[][] tabuleiro, Peca peca, Integer tipoPecaAdversaria ){
		return removeuPecaDoTabuleiro( tabuleiro, peca, tipoPecaAdversaria, peca.getColuna() - 1, peca.getLinha() + 1 );
	}

	private static Boolean atacouAdversariaInferiorDireita( Casa[][] tabuleiro, Peca peca, Integer tipoPecaAdversaria ){
		return removeuPecaDoTabuleiro( tabuleiro, peca, tipoPecaAdversaria, peca.getColuna() + 1, peca.getLinha() + 1 );
	}

	private static Boolean atacouAdversariaSuperiorDireita( Casa[][] tabuleiro, Peca peca, Integer tipoPecaAdversaria ){
		return removeuPecaDoTabuleiro( tabuleiro, peca, tipoPecaAdversaria, peca.getColuna() + 1, peca.getLinha() - 1 );
	}


	private static Boolean removeuPecaDoTabuleiro( Casa[][] tabuleiro, Peca peca, Integer tipoPecaAdversaria, Integer coluna, Integer linha ){
		Integer damaAdversaria	= tipoPecaAdversaria.equals( Peca.PECA_BRANCA ) ? Peca.DAMA_BRANCA : Peca.DAMA_PRETA;

		try {
			if( ( tabuleiro[ coluna ][ linha ].getPeca().getTipoPeca().equals( tipoPecaAdversaria ) ) ||
				( tabuleiro[ coluna ][ linha ].getPeca().getTipoPeca().equals( damaAdversaria ) )	){
				tabuleiro[ coluna ][ linha ] = new Casa( coluna, linha, null );
				return true;
			}

		} catch( ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException ){
			return false;

		} catch( NullPointerException nullPointerException ){
			return false;
		}
		
		return false;
	}

	private static Boolean pecaAvancandoEmX(Integer deslocamentoEmX) {
		return deslocamentoEmX > 0;
	}

	private static Boolean pecaVoltandoEmX( Integer deslocamentoEmX ){
		return deslocamentoEmX < 0;
	}

	private static Boolean pecaSubindoEmY( Integer deslocamentoEmY ){
		return deslocamentoEmY < 0;
	}

	private static Boolean pecaDescendoEmY( Integer deslocamentoEmY ){
		return deslocamentoEmY > 0;
	}
}
